
#include "nane/testframework/test0/test0.h"
#include "nane/RenderSystem/Texture.h"
#include "nane/CodecSystem/Image/ImageDecoder.h"

namespace nane
{
    ////////////////////////////////////////////////////////////////////////////////////////////////
    TestFramework* TestFramework::CreateTest()
    {
        return new Test0();
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void TestFramework::ReleaseTest( TestFramework* _test )
    {
        delete static_cast<Test0*>( _test );
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void Test0::OnSetup()
    {
        m_renderSystem.SetLightingEnable(false);
        m_renderSystem.SetCullMode(ECullMode::CounterClockwise);
        VertexElement vertexDecl[] = {
              {0, 0, EVertexElementSemantic::Position, EVertexElementType::Float3, 0}
            , {0, 12, EVertexElementSemantic::Normal, EVertexElementType::Float3, 0}
            //, {0, 24, VES_COLOR, EVertexElementType::Color, 0}
            , {0, 28, EVertexElementSemantic::TexCoord, EVertexElementType::Float2, 0} 
        };
        
        struct CUSTOMVERTEX
        {
            float x, y, z;      // The untransformed position for the vertex.
            float nx, ny, nz;   // normal
            uint32 color;       // The vertex color.
            float tu, tv;       // texture coords
        };

        CUSTOMVERTEX vertices[] =
        {
            // Front Face
            {-1.0f, 1.0f,-1.0f,  0.0f, 0.0f,-1.0f,  0xff0000ff, 0.0f,0.0f },
            { 1.0f, 1.0f,-1.0f,  0.0f, 0.0f,-1.0f,  0xff0000ff, 1.0f,0.0f },
            {-1.0f,-1.0f,-1.0f,  0.0f, 0.0f,-1.0f,  0xffff0000, 0.0f,1.0f },
            { 1.0f,-1.0f,-1.0f,  0.0f, 0.0f,-1.0f,  0xffff0000, 1.0f,1.0f },
            // Back Face
            {-1.0f, 1.0f, 1.0f,  0.0f, 0.0f, 1.0f,  0xffffffff, 1.0f,0.0f },
            {-1.0f,-1.0f, 1.0f,  0.0f, 0.0f, 1.0f,  0xffffffff, 1.0f,1.0f },
            { 1.0f, 1.0f, 1.0f,  0.0f, 0.0f, 1.0f,  0xffffffff, 0.0f,0.0f },
            { 1.0f,-1.0f, 1.0f,  0.0f, 0.0f, 1.0f,  0xffffffff, 0.0f,1.0f },
            // Top Face
            {-1.0f, 1.0f, 1.0f,  0.0f, 1.0f, 0.0f,  0xffffffff, 0.0f,0.0f },
            { 1.0f, 1.0f, 1.0f,  0.0f, 1.0f, 0.0f,  0xffffffff, 1.0f,0.0f },
            {-1.0f, 1.0f,-1.0f,  0.0f, 1.0f, 0.0f,  0xff0000ff, 0.0f,1.0f },
            { 1.0f, 1.0f,-1.0f,  0.0f, 1.0f, 0.0f,  0xff0000ff, 1.0f,1.0f },
            // Bottom Face
            {-1.0f,-1.0f, 1.0f,  0.0f,-1.0f, 0.0f,  0xffffffff, 0.0f,1.0f },
            {-1.0f,-1.0f,-1.0f,  0.0f,-1.0f, 0.0f,  0xffff0000, 0.0f,0.0f },
            { 1.0f,-1.0f, 1.0f,  0.0f,-1.0f, 0.0f,  0xffffffff, 1.0f,1.0f },
            { 1.0f,-1.0f,-1.0f,  0.0f,-1.0f, 0.0f,  0xffff0000, 1.0f,0.0f },
            // Right Face
            { 1.0f, 1.0f,-1.0f,  1.0f, 0.0f, 0.0f,  0xff0000ff, 0.0f,0.0f },
            { 1.0f, 1.0f, 1.0f,  1.0f, 0.0f, 0.0f,  0xffffffff, 1.0f,0.0f },
            { 1.0f,-1.0f,-1.0f,  1.0f, 0.0f, 0.0f,  0xffff0000, 0.0f,1.0f },
            { 1.0f,-1.0f, 1.0f,  1.0f, 0.0f, 0.0f,  0xffffffff, 1.0f,1.0f },
            // Left Face
            {-1.0f, 1.0f,-1.0f, -1.0f, 0.0f, 0.0f,  0xff0000ff, 1.0f,0.0f },
            {-1.0f,-1.0f,-1.0f, -1.0f, 0.0f, 0.0f,  0xffff0000, 1.0f,1.0f },
            {-1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f,  0xffffffff, 0.0f,0.0f },
            {-1.0f,-1.0f, 1.0f, -1.0f, 0.0f, 0.0f,  0xffffffff, 0.0f,1.0f }

        };

        uint16 indicies[] = { 0, 1, 2, 1, 3, 2, 4, 5, 6, 5, 7, 6, 8, 9, 10, 9, 11, 10, 12, 13, 14, 13, 15, 14, 16, 17, 18, 17, 19, 18, 20, 21, 22, 21, 23, 22 };

        m_pVB = m_renderSystem.CreateVertexBuffer(EResourceUsage::Default, 24, sizeof(CUSTOMVERTEX), vertexDecl, 3, reinterpret_cast<byte*>(vertices));
        m_renderSystem.SetVertexBuffer(0, m_pVB);

        m_pIB = m_renderSystem.CreateIndexBuffer(EResourceUsage::Default, 36, indicies);
        m_renderSystem.SetIndexBuffer(m_pIB);

        //ReadFilePtr textureFile = m_assets->openFile("assets/test0/nane.png");
        //ImageDecoderPtr imageDecoder = ImageCodecManager::instance("assets/test0/nane.png")->createDecoder(textureFile);
        
        m_pTexture = m_renderSystem.CreateTexture(EResourceUsage::Default, 64, 64, EPixelFormat::A8R8G8B8);
        uint32 pitch = 0;
        byte* textureData = m_pTexture.Lock(0, 0, 64, 64, &pitch);
        if( textureData != NULL )
        {
            for( int i = 0; i < 32; ++i )
            {
                //uint32 color = (0xff << (i % 8)*2);
                memset(textureData, 0x00000000, 32*4);
                memset(textureData + 32*4, 0xffffffff, 32*4);
                //std::fill(reinterpret_cast<uint32*>(textureData + 32*4), reinterpret_cast<uint32*>(textureData + 64*4), color);
                textureData += pitch;
            }
            for( int i = 0; i < 32; ++i )
            {
                uint32 color = (0xff << (((31-i) / 8)*8));
                //uint32 color = i * 8;
                std::fill(reinterpret_cast<uint32*>(textureData), reinterpret_cast<uint32*>(textureData) + 32, color);
                //memset(textureData, 0xffffffff, 32*4);
                memset(textureData + 32*4, 0x00000000, 32*4);
                textureData += pitch;
            }
        }
//        for( uint32 i = 0; i < imageDecoder->getHeight(); ++i )
//        {
//            imageDecoder->readRow(textureData, 4*imageDecoder->getWidth());
//            textureData += pitch;
//        }
        m_pTexture.Unlock();
        //imageDecoder = NullPtr;
        m_renderSystem.SetTexture(0, m_pTexture);
        
        cml::matrix44f_r proj;
        cml::matrix_perspective_xfov_LH(proj, cml::constantsf::pi_over_4(), static_cast<float>(m_windowWidth) / m_windowHeight, 1.0f, 10.0f, cml::z_clip_zero);
        m_renderSystem.SetProjectionMatrix(proj.data());

        cml::vector3f vEyePt   ( 0.0f, 3.0f,-5.0f );
        cml::vector3f vLookatPt( 0.0f, 0.0f, 0.0f );
        cml::vector3f vUpVec   ( 0.0f, 1.0f, 0.0f );
        cml::matrix_look_at_LH(m_view, vEyePt, vLookatPt, vUpVec);
        m_renderSystem.SetModelViewMatrix(m_view.data());
        m_world.identity();
        m_rotating = true;

        m_scriptModule = m_scriptSystem.LoadModule("Test0", m_assets.OpenFile("assets/test0/test0.lua"));
        m_scriptModule.ExportMemberFunction("Test0", "ToggleRotate", &Test0::ToggleRotate, this);
        m_scriptModule.ExportMemberFunction("Test0", "RotateY", &Test0::RotateY, this);
        m_scriptModule.ExportMemberFunction("Test0", "RotateX", &Test0::RotateX, this);
        
        m_scriptModule.ImportObject("main").Invoke<void>();

        m_inputSystem.AddActionBinding("ToggleRotate", InputBinding(EMouseAxis::LeftButton));
        m_inputSystem.AddActionBinding("RotateY", InputBinding(EMouseAxis::RelX, -0.001f), InputBindingModifier(EMouseAxis::RightButton));
        m_inputSystem.AddActionBinding("RotateX", InputBinding(EMouseAxis::RelY, -0.001f), InputBindingModifier(EMouseAxis::RightButton));
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void Test0::OnUpdate( uint32 _dt )
    {
        if( m_inputSystem.GetActionWasReleased("ToggleRotate") > 0 )
        {
            ToggleRotate();
        }
        if( m_rotating )
        {
            cml::matrix_rotate_about_world_y(m_world, _dt * 0.001f);
        }
        else
        {
            RotateY(m_inputSystem.GetActionValue("RotateY"));
            RotateX(m_inputSystem.GetActionValue("RotateX"));
        }
        cml::matrix44f_r modelview = m_world * m_view;
        m_renderSystem.SetModelViewMatrix(modelview.data());
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void Test0::RotateY( float _value )
    {
        if( m_rotating == false )
        {
            cml::matrix_rotate_about_world_y(m_world, _value);
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void Test0::RotateX( float _value )
    {
        if( m_rotating == false )
        {
            cml::matrix_rotate_about_world_x(m_world, _value);
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void Test0::ToggleRotate()
    {
        m_rotating = !m_rotating;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void Test0::OnRender()
    {
        m_renderSystem.ClearRenderTarget(EFrameBufferType::Color | EFrameBufferType::Depth, 0xFF00FFFF, 1.0f, 0);
        m_renderSystem.BeginScene();
        m_renderSystem.DrawIndexedPrimitive(EPrimitiveType::TriangleList, 0, 0, 24, 0, 12);
        m_renderSystem.EndScene();
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void Test0::OnShutdown()
    {
        m_scriptModule = NullPtr;
        m_pTexture = NullPtr;
        m_pIB = NullPtr;
        m_pVB = NullPtr;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
}   // namespace nane
